<template>
    <view>
        <!-- 使用transition组件包裹弹窗 -->
        <transition name="slide-up">
            <!-- 录音弹窗 -->
            <view class="modal-container" v-if="visible">
                <!-- 背景遮罩 -->
                <view class="modal-mask" @click="closeModal"></view>
                
                <!-- 弹窗内容 -->
                <view class="modal-content">
                    <view class="modal-header">
                        <text class="modal-title">录音</text>
                        <text class="modal-close" @click="closeModal">×</text>
                    </view>
                    
                    <view class="modal-body">
                        <!-- 加载指示器 -->
                        <view class="loading-container" v-if="isRecognizing">
                            <view class="loading-spinner"></view>
                            <text class="loading-text">正在识别文字</text>
                        </view>
                        
                        <view class="image-container">
                            <span v-if="isRecording" class="ripple-third-layer"></span>
                            <span v-if="isRecording" class="ripple-fourth-layer"></span>
                            <span v-if="isRecording" class="ripple-fifth-layer"></span>
                            <image 
                                src="/static/say.png" 
                                mode=""
                                @touchstart="onTouchStart"
                                @touchend="onTouchEnd"
                                @touchmove="onTouchMove"
                                @contextmenu.prevent
                                @touchstart.prevent
                                class="record-image"
                                :class="{'ripple-effect': isRecording}"
                                :data-value="isRecording ? 1 : 0"
                            >
                            </image>
                        </view>
                        <!-- 移除取消文字 -->
                        <view class="record-hint"></view>
                        <view class="recording-status" :class="{'status-canceled': recordingStatus === '取消'}">
                            <template v-if="recordingStatus === '未录音'">长按说话</template>
                            <template v-else-if="recordingStatus === '录音已取消'">取消</template>
                            <template v-else-if="recordingStatus === '录音已停止'">说话已结束</template>
                            <template v-else>{{ recordingStatus }}</template>
                        </view>
                        
                        <!-- 显示识别状态 -->
                        <view v-if="recognitionStatus" class="recognition-status" :class="{'success-status': recognitionStatus === '文字识别成功', 'failed-status': recognitionStatus === '文字识别失败'}">
                            {{ recognitionStatus }}
                        </view>
                        
                        <!-- 添加input输入框显示语音识别结果 -->
                        <input 
                            class="recognition-result-input" 
                            v-if="recognitionResult" 
                            v-model="recognitionResult" 
                            disabled 
                            placeholder="语音识别结果将显示在这里" 
                        />
                        <!-- 隐藏上传WAV文件按钮 -->
                        <button @click="uploadWavFile" :disabled="!recordedBlob" class="upload-btn" v-show="false">上传WAV文件</button>
                        <!-- 音频播放组件 - 隐藏 -->
                        <audio ref="audioPlayer" controls :src="audioUrl" style="margin-top: 20px; width: 100%; display: none;"></audio>
                    </view>
                </view>
            </view>
        </transition>
    </view>
</template>

<script>

	
export default {
    name: 'VoiceRecorder',
    props: {
        visible: {
            type: Boolean,
            default: false
        }
    },
    data() {
        return {
            isRecording: false,
            recordingStatus: '未录音',
            mediaRecorder: null,
            recordedBlob: null,
            chunks: [],
            audioUrl: '',
            startY: 0,
            cancelThreshold: 50, // 上滑取消的阈值
            isCanceling: false,
            // 存储file值
            fileValue: '',
            // 存储请求参数和响应结果
            requestParams: '暂无请求参数',
            responseResult: '暂无响应结果',
            // 更改默认的MIME类型为WAV格式
            mimeType: 'audio/wav',
            // 存储特定格式的文件名
            formattedFileName: '',
            // 加载状态
            isRecognizing: false,
            // API响应消息
            recognitionResult: '',
            // 识别状态信息
            recognitionStatus: ''
        };
    },
    watch: {
        // 监听visible属性变化，当弹窗打开时初始化
        visible(newVal) {
            if (newVal) {
                this.initializeRecorder();
            }
        }
    },
    methods: {
        // 初始化录音器
        initializeRecorder() {
            // 重置所有状态
            this.isRecording = false;
            this.recordingStatus = '未录音';
            this.mediaRecorder = null;
            this.recordedBlob = null;
            this.chunks = [];
            this.audioUrl = '';
            this.isCanceling = false;
            this.fileValue = '';
            this.requestParams = '暂无请求参数';
            this.responseResult = '暂无响应结果';
            this.isRecognizing = false;
            this.recognitionResult = '';
            this.recognitionStatus = '';
        },
        
        // 关闭录音弹窗
        closeModal() {
            // 如果正在录音，先停止录音
            if (this.isRecording && this.mediaRecorder) {
                this.mediaRecorder.stop();
                this.isRecording = false;
                this.recordingStatus = '取消';
            }
            
            // 发送识别状态到父组件，以便在关闭后显示提示
            if (this.recognitionStatus) {
                this.$emit('recognition-status', this.recognitionStatus);
            }
            
            this.$emit('close');
        },
        
        onTouchStart(event) {
            event.preventDefault();
            if (!this.isMediaRecorderSupported()) {
                console.error('浏览器不支持 MediaRecorder API');
                this.recordingStatus = '浏览器不支持录音';
                return;
            }
            this.startY = event.touches[0].clientY;
            this.isCanceling = false;
            navigator.mediaDevices.getUserMedia({ audio: true })
              .then((stream) => {
                    // 确定支持的MIME类型
                    this.determineMimeType();
                    
                    this.mediaRecorder = new MediaRecorder(stream, { mimeType: this.mimeType });
                    this.mediaRecorder.ondataavailable = (event) => {
                        if (event.data.size > 0) {
                            this.chunks.push(event.data);
                        }
                    };
                    this.mediaRecorder.onstop = () => {
                        if (!this.isCanceling) {
                            // 生成特定格式的文件名
                            this.formattedFileName = this.generateFormattedFileName();
                            
                            // 使用记录的MIME类型创建Blob
                            this.recordedBlob = new Blob(this.chunks, { type: this.mimeType });
                            // 生成可播放的URL
                            this.audioUrl = URL.createObjectURL(this.recordedBlob);
                            console.log('录音已完成，音频URL:', this.audioUrl);
                            console.log('录音MIME类型:', this.mimeType);
                            console.log('生成的文件名:', this.formattedFileName);
                        }
                        this.chunks = [];
                        // 关闭音轨
                        stream.getTracks().forEach(track => track.stop());
                    };
                    this.mediaRecorder.start();
                    this.isRecording = true;
                    this.recordingStatus = '正在说话';
                })
              .catch((error) => {
                    console.error('获取麦克风权限失败:', error);
                    this.recordingStatus = '获取权限失败';
                });
        },
        
        onTouchMove(event) {
            event.preventDefault();
            const currentY = event.touches[0].clientY;
            if (this.isRecording && this.startY - currentY > this.cancelThreshold) {
                this.isCanceling = true;
                if (this.mediaRecorder) {
                    this.mediaRecorder.stop();
                    this.isRecording = false;
                    this.recordingStatus = '取消';
                    
                    // 1秒后显示"未录音"状态
                    setTimeout(() => {
                        this.recordingStatus = '未录音';
                    }, 1000);
                }
            }
        },
        
        onTouchEnd() {
            if (this.isRecording && !this.isCanceling) {
                if (this.mediaRecorder) {
                    this.mediaRecorder.stop();
                    this.isRecording = false;
                    this.recordingStatus = '说话已结束';
                    
                    // 添加延迟，等待录音完成后自动上传
                    setTimeout(() => {
                        if (this.recordedBlob) {
                            this.uploadWavFile();
                        }
                    }, 500); // 500ms延迟确保recordedBlob已经生成
                }
            }
        },
        
        uploadWavFile() {
            this.uploadAsBlob();
        },
        
        uploadAsBlob() {
            if (this.recordedBlob) {
                // 使用已经格式化好的文件名
                const fileName = this.formattedFileName || this.generateFormattedFileName();
                
                // 显示请求参数
                this.requestParams = `类型: Blob文件\n文件名: ${fileName}\n类型: ${this.recordedBlob.type}\n大小: ${this.recordedBlob.size} 字节`;
                
                // 显示file的值
                this.fileValue = '(binary) 音频文件\n类型: ' + this.recordedBlob.type + 
                               '\n大小: ' + (this.recordedBlob.size / 1024).toFixed(2) + ' KB' +
                               '\n文件名: ' + fileName;
                
                // 尝试上传
                this.tryUploadWithFormData(fileName);
            }
        },
        
        // 使用FormData尝试上传
        tryUploadWithFormData(fileName) {
            try {
                // 设置加载状态
                this.isRecognizing = true;
                
                const formData = new FormData();
                
                // 不要尝试强制转换格式，直接上传原始格式
                // 检查当前录音格式
                formData.append('file', this.recordedBlob, fileName);
                console.log(`上传音频文件: ${fileName}, 类型: ${this.recordedBlob.type}, 大小: ${this.recordedBlob.size} 字节`);
                
                const xhr = new XMLHttpRequest();
                xhr.open('POST', 'http://120.76.99.113:7106/api/common/audioUploadFileIdentify');
                
                // 添加Content-Type头，确保不会被服务器错误解析
                xhr.setRequestHeader('X-File-Type', this.recordedBlob.type);
                
                // 设置请求超时
                xhr.timeout = 30000; // 30秒
                
                // 显示更多错误信息
                xhr.onload = () => {
                    // 结束加载状态
                    this.isRecognizing = false;
                    
                    if (xhr.status === 200) {
                        try {
                            const result = JSON.parse(xhr.responseText);
                            console.log('文件上传成功，返回内容:', result);
                            this.responseResult = JSON.stringify(result, null, 2);
                            
                            // 将API响应消息显示在主页面上
                            if (result && result.data && result.data.msg) {
                                this.recognitionResult = result.data.msg;
                                // 发送识别结果到父组件
                                this.$emit('recognition-result', result.data.msg);
                                
                                // 根据响应状态码判断识别是否成功
                                if (result.code === 200) {
                                    console.log('识别成功');
                                    this.recognitionStatus = '文字识别成功';
                                    // 延迟关闭弹窗，给用户一些时间看到识别状态变化
                                    setTimeout(() => {
                                        this.closeModal();
                                    }, 1000);
                                } else {
                                    console.log('识别失败，状态码:', result.code);
                                    this.recognitionStatus = '文字识别失败';
                                }
                            } else if (result && result.msg !== undefined) {
                                // 处理顶层msg字段的情况
                                this.recognitionResult = result.msg;
                                // 发送识别结果到父组件
                                this.$emit('recognition-result', result.msg);
                                
                                // 根据响应状态码判断识别是否成功
                                if (result.code === 200) {
                                    console.log('识别成功');
                                    this.recognitionStatus = '文字识别成功';
                                    // 延迟关闭弹窗，给用户一些时间看到识别状态变化
                                    setTimeout(() => {
                                        this.closeModal();
                                    }, 1000);
                                } else {
                                    console.log('识别失败，状态码:', result.code);
                                    this.recognitionStatus = '文字识别失败';
                                }
                            } else {
                                // 如果没有找到msg字段，直接展示整个响应
                                this.recognitionResult = JSON.stringify(result);
                                // 发送识别结果到父组件
                                this.$emit('recognition-result', JSON.stringify(result));
                            }
                        } catch (e) {
                            console.error('解析响应JSON失败:', e);
                            this.responseResult = '解析响应失败: ' + xhr.responseText;
                            // 如果FormData方法失败，尝试使用uni-app方法
                            this.tryUploadWithUniApp(fileName);
                        }
                    } else {
                        console.error(`请求失败: HTTP状态码 ${xhr.status}, 响应: ${xhr.responseText}`);
                        this.responseResult = `请求失败: HTTP状态码 ${xhr.status}\n响应内容: ${xhr.responseText}`;
                        // 如果FormData方法失败，尝试使用uni-app方法
                        this.tryUploadWithUniApp(fileName);
                    }
                };
                
                xhr.ontimeout = () => {
                    // 结束加载状态
                    this.isRecognizing = false;
                    
                    console.error('请求超时');
                    this.responseResult = '请求超时，服务器未响应';
                    // 如果FormData方法失败，尝试使用uni-app方法
                    this.tryUploadWithUniApp(fileName);
                };
                
                xhr.onerror = (error) => {
                    // 结束加载状态
                    this.isRecognizing = false;
                    
                    console.error('文件上传网络错误:', error);
                    this.responseResult = '网络错误，请求失败';
                    // 如果FormData方法失败，尝试使用uni-app方法
                    this.tryUploadWithUniApp(fileName);
                };
                
                // 添加进度事件
                xhr.upload.onprogress = (event) => {
                    if (event.lengthComputable) {
                        const percentComplete = Math.round((event.loaded / event.total) * 100);
                        console.log(`上传进度: ${percentComplete}%`);
                    }
                };
                
                // 发送FormData
                xhr.send(formData);
            } catch (error) {
                // 结束加载状态
                this.isRecognizing = false;
                
                console.error('处理文件上传时出错:', error);
                this.responseResult = '处理文件时出错: ' + JSON.stringify(error);
                // 如果FormData方法失败，尝试使用uni-app方法
                this.tryUploadWithUniApp(fileName);
            }
        },
        
        // 使用uni-app的方式尝试上传
        tryUploadWithUniApp(fileName) {
            // 设置加载状态
            this.isRecognizing = true;
            
            console.log('尝试使用uni-app方式上传');
            
            // 使用已格式化的文件名，不需要再修改扩展名
            
            // 首先将Blob转换为本地临时文件
            const tempFilePath = this.audioUrl; // 这是之前创建的本地URL

            // 使用uni.uploadFile
            uni.uploadFile({
                url: 'http://120.76.99.113:7106/api/common/audioUploadFileIdentify',
                filePath: tempFilePath,
                name: 'file',
                header: {
                    'X-File-Type': this.recordedBlob.type  // 添加自定义头部传递原始MIME类型
                },
                formData: {
                    fileName: fileName,
                    fileType: this.recordedBlob.type  // 在表单数据中也传递MIME类型
                },
                success: (res) => {
                    // 结束加载状态
                    this.isRecognizing = false;
                    
                    console.log('uni-app上传成功:', res);
                    try {
                        const result = JSON.parse(res.data);
                        this.responseResult = JSON.stringify(result, null, 2);
                        
                        // 将API响应消息显示在主页面上
                        if (result && result.data && result.data.msg) {
                            this.recognitionResult = result.data.msg;
                            // 发送识别结果到父组件
                            this.$emit('recognition-result', result.data.msg);
                            
                            // 根据响应状态码判断识别是否成功
                            if (result.code === 200) {
                                console.log('识别成功');
                                this.recognitionStatus = '文字识别成功';
                                // 延迟关闭弹窗，给用户一些时间看到识别状态变化
                                setTimeout(() => {
                                    this.closeModal();
                                }, 1000);
                            } else {
                                console.log('识别失败，状态码:', result.code);
                                this.recognitionStatus = '文字识别失败';
                            }
                        } else if (result && result.msg !== undefined) {
                            // 处理顶层msg字段的情况
                            this.recognitionResult = result.msg;
                            // 发送识别结果到父组件
                            this.$emit('recognition-result', result.msg);
                            
                            // 根据响应状态码判断识别是否成功
                            if (result.code === 200) {
                                console.log('识别成功');
                                this.recognitionStatus = '文字识别成功';
                                // 延迟关闭弹窗，给用户一些时间看到识别状态变化
                                setTimeout(() => {
                                    this.closeModal();
                                }, 1000);
                            } else {
                                console.log('识别失败，状态码:', result.code);
                                this.recognitionStatus = '文字识别失败';
                            }
                        } else {
                            // 如果没有找到msg字段，直接展示整个响应
                            this.recognitionResult = JSON.stringify(result);
                            // 发送识别结果到父组件
                            this.$emit('recognition-result', JSON.stringify(result));
                        }
                    } catch (e) {
                        this.responseResult = '解析响应失败: ' + res.data;
                    }
                },
                fail: (err) => {
                    // 结束加载状态
                    this.isRecognizing = false;
                    
                    console.error('uni-app上传失败:', err);
                    this.responseResult = '上传失败: ' + JSON.stringify(err);
                    
                    // 如果uni.uploadFile也失败，尝试将Blob转换为ArrayBuffer再上传
                    this.tryUploadWithArrayBuffer(fileName);
                }
            });
        },
        
        // 将Blob转换为ArrayBuffer再上传
        tryUploadWithArrayBuffer(fileName) {
            // 设置加载状态
            this.isRecognizing = true;
            
            console.log('尝试使用ArrayBuffer方式上传');
            
            // 使用已格式化的文件名，不需要再修改扩展名
            
            // 将Blob转换为ArrayBuffer
            const reader = new FileReader();
            reader.onload = (e) => {
                const arrayBuffer = e.target.result;
                
                // 使用uni.request上传ArrayBuffer
                uni.request({
                    url: 'http://120.76.99.113:7106/api/common/audioUploadFileIdentify',
                    method: 'POST',
                    header: {
                        'Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundary' + Math.random().toString(16).substr(2),
                        'accept': 'application/json'
                    },
                    data: {
                        file: arrayBuffer,
                        fileName: fileName
                    },
                    success: (res) => {
                        // 结束加载状态
                        this.isRecognizing = false;
                        
                        console.log('ArrayBuffer上传成功:', res);
                        try {
                            const result = res.data;
                            this.responseResult = JSON.stringify(result, null, 2);
                            
                            // 将API响应消息显示在主页面上
                            if (result && result.data && result.data.msg) {
                                this.recognitionResult = result.data.msg;
                                // 发送识别结果到父组件
                                this.$emit('recognition-result', result.data.msg);
                                
                                // 根据响应状态码判断识别是否成功
                                if (result.code === 200) {
                                    console.log('识别成功');
                                    this.recognitionStatus = '文字识别成功';
                                    // 延迟关闭弹窗，给用户一些时间看到识别状态变化
                                    setTimeout(() => {
                                        this.closeModal();
                                    }, 1000);
                                } else {
                                    console.log('识别失败，状态码:', result.code);
                                    this.recognitionStatus = '文字识别失败';
                                }
                            } else if (result && result.msg !== undefined) {
                                // 处理顶层msg字段的情况
                                this.recognitionResult = result.msg;
                                // 发送识别结果到父组件
                                this.$emit('recognition-result', result.msg);
                                
                                // 根据响应状态码判断识别是否成功
                                if (result.code === 200) {
                                    console.log('识别成功');
                                    this.recognitionStatus = '文字识别成功';
                                    // 延迟关闭弹窗，给用户一些时间看到识别状态变化
                                    setTimeout(() => {
                                        this.closeModal();
                                    }, 1000);
                                } else {
                                    console.log('识别失败，状态码:', result.code);
                                    this.recognitionStatus = '文字识别失败';
                                }
                            } else {
                                // 如果没有找到msg字段，直接展示整个响应
                                this.recognitionResult = JSON.stringify(result);
                                // 发送识别结果到父组件
                                this.$emit('recognition-result', JSON.stringify(result));
                            }
                        } catch (e) {
                            this.responseResult = '解析响应失败: ' + JSON.stringify(res);
                        }
                    },
                    fail: (err) => {
                        // 结束加载状态
                        this.isRecognizing = false;
                        
                        console.error('ArrayBuffer上传失败:', err);
                        this.responseResult = '所有上传方法均失败。请检查录音格式是否为WAV或联系开发人员。最后错误: ' + JSON.stringify(err);
                    }
                });
            };
            reader.onerror = (error) => {
                // 结束加载状态
                this.isRecognizing = false;
                
                console.error('读取Blob为ArrayBuffer失败:', error);
                this.responseResult = '读取文件失败: ' + JSON.stringify(error);
            };
            reader.readAsArrayBuffer(this.recordedBlob);
        },
        
        isMediaRecorderSupported() {
            return typeof MediaRecorder !== 'undefined';
        },
        
        // 确定浏览器支持的MIME类型
        determineMimeType() {
            // 按优先级顺序列出支持的MIME类型，使用浏览器最佳支持的格式
            const preferredTypes = [
                'audio/webm;codecs=opus',  // 大多数现代浏览器都支持这种格式
                'audio/webm',
                'audio/ogg;codecs=opus',
                'audio/ogg',
                'audio/mp4',
                'audio/mpeg',
                'audio/wav',
                'audio/x-wav'
            ];
            
            for (let type of preferredTypes) {
                if (MediaRecorder.isTypeSupported(type)) {
                    this.mimeType = type;
                    console.log('使用MIME类型:', type);
                    return;
                }
            }
            
            // 如果没有匹配到，保留默认值
            console.log('未找到支持的MIME类型，使用默认值:', this.mimeType);
        },
        
        // 根据MIME类型获取合适的文件扩展名
        getFileExtension(mimeType) {
            const mimeToExt = {
                'audio/webm': 'webm',
                'audio/webm;codecs=opus': 'webm',
                'audio/ogg': 'ogg',
                'audio/ogg;codecs=opus': 'ogg',
                'audio/mp4': 'mp4',
                'audio/mpeg': 'mp3',
                'audio/wav': 'wav',
                'audio/x-wav': 'wav'
            };
            
            return mimeToExt[mimeType] || 'webm'; // 如果未知MIME类型，默认使用webm
        },
        
        // 生成特定格式的文件名
        generateFormattedFileName() {
            const now = new Date();
            // 格式化日期为 yyyyMMddHHmmss
            const year = now.getFullYear();
            const month = String(now.getMonth() + 1).padStart(2, '0');
            const day = String(now.getDate()).padStart(2, '0');
            const hours = String(now.getHours()).padStart(2, '0');
            const minutes = String(now.getMinutes()).padStart(2, '0');
            const seconds = String(now.getSeconds()).padStart(2, '0');
            
            // 添加一个随机标识符
            const randomId = Math.floor(Math.random() * 1000).toString().padStart(3, '0');
            
            // 获取正确的文件扩展名
            const ext = this.getFileExtension(this.mimeType);
            
            // 组合成最终格式，使用正确的扩展名
            return `recording ${year}${month}${day}${hours}${minutes}${seconds}A${randomId}.${ext}`;
        },
    }
}
</script>

<style>
/* 模态弹窗容器 */
.modal-container {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1000;
    display: flex;
    justify-content: center;
    align-items: flex-end;
}

/* 背景遮罩层 */
.modal-mask {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background-color: rgba(0, 0, 0, 0.45);
    z-index: 1001;
}

/* 弹窗内容 */
.modal-content {
    position: relative;
    width: 100%;
    background-color: white;
    border-radius: 16px 16px 0 0;
    padding: 20px;
    box-shadow: 0px -5px 15px rgba(0, 0, 0, 0.1);
    z-index: 1002;
    transition: transform 0.3s ease;
}

/* 弹窗头部样式 */
.modal-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 15px;
}

/* 弹窗标题 */
.modal-title {
    font-size: 18px;
    font-weight: bold;
    color: #333;
}

/* 弹窗关闭按钮 */
.modal-close {
    font-size: 24px;
    color: #999;
    padding: 5px;
}

/* 弹窗主体内容 */
.modal-body {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 15px 0;
    min-height: 250px;
}

/* 录音图像容器 */
.image-container {
    position: relative;
    width: 200px;  /* 原来是100px，放大一倍 */
    height: 200px; /* 原来是100px，放大一倍 */
    margin: 20px 0 5px; /* 减小底部边距 */
    display: flex;
    justify-content: center;
    align-items: center;
}

/* 录音图标 */
.record-image {
    width: 160px;  /* 原来是80px，放大一倍 */
    height: 160px; /* 原来是80px，放大一倍 */
    z-index: 10;
    border-radius: 50%;
    box-shadow: 0 0 10px rgba(0, 122, 255, 0.3);
    transition: transform 0.2s, box-shadow 0.2s;
}

/* 第三层水波纹背景 */
.ripple-third-layer {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 320px; /* 原来是160px，放大一倍 */
    height: 320px; /* 原来是160px，放大一倍 */
    border-radius: 50%;
    background: radial-gradient(circle, rgba(0, 122, 255, 0.3) 0%, rgba(0, 122, 255, 0) 70%);
    z-index: 0;
    will-change: transform, opacity;
    animation: smooth-ripple-3 3s cubic-bezier(0.25, 0.46, 0.45, 0.94) infinite;
}

/* 第四层水波纹背景 */
.ripple-fourth-layer {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 360px; /* 原来是180px，放大一倍 */
    height: 360px; /* 原来是180px，放大一倍 */
    border-radius: 50%;
    background: radial-gradient(circle, rgba(0, 122, 255, 0.2) 0%, rgba(0, 122, 255, 0) 70%);
    z-index: 0;
    will-change: transform, opacity;
    animation: smooth-ripple-4 4s cubic-bezier(0.25, 0.46, 0.45, 0.94) infinite;
    animation-delay: 0.5s;
}

/* 第五层水波纹背景 */
.ripple-fifth-layer {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 400px; /* 原来是200px，放大一倍 */
    height: 400px; /* 原来是200px，放大一倍 */
    border-radius: 50%;
    background: radial-gradient(circle, rgba(0, 122, 255, 0.15) 0%, rgba(0, 122, 255, 0) 70%);
    z-index: 0;
    will-change: transform, opacity;
    animation: smooth-ripple-5 5s cubic-bezier(0.25, 0.46, 0.45, 0.94) infinite;
    animation-delay: 1s;
}

/* 水波纹效果 - 显著增强版 */
.ripple-effect {
    position: relative;
    z-index: 10;
    transform-origin: center center;
    /* 轻微脉动效果，更平滑 */
    animation: smooth-pulse 2s ease-in-out infinite;
}

/* 第一层水波纹 */
.ripple-effect::before {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: 200px; /* 原来是100px，放大一倍 */
    height: 200px; /* 原来是100px，放大一倍 */
    transform: translate(-50%, -50%);
    border-radius: 50%;
    background: radial-gradient(circle, rgba(0, 122, 255, 0.7) 0%, rgba(0, 122, 255, 0) 70%);
    z-index: 9;
    will-change: transform, opacity;
    box-shadow: 0 0 15px rgba(0, 122, 255, 0.5);
    animation: smooth-ripple-1 2.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) infinite;
}

/* 第二层水波纹 */
.ripple-effect::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: 260px; /* 原来是130px，放大一倍 */
    height: 260px; /* 原来是130px，放大一倍 */
    transform: translate(-50%, -50%);
    border-radius: 50%;
    background: radial-gradient(circle, rgba(0, 122, 255, 0.5) 0%, rgba(0, 122, 255, 0) 70%);
    z-index: 8;
    will-change: transform, opacity;
    animation: smooth-ripple-2 2.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) infinite;
    animation-delay: 0.3s;
}

/* 录音提示文字 */
.record-hint {
    margin: 10px 0 5px;
    font-size: 14px;
    color: #666;
    text-align: center;
    height: 20px; /* 保持高度以不影响布局 */
}

/* 录音状态 */
.recording-status {
    margin: 5px 0; /* 原来是15px 0，减小上下边距 */
    font-size: 16px;
    color: #333;
}

/* 录音取消状态样式 */
.status-canceled {
    color: #ff3b30;
    font-weight: bold;
}

/* 识别状态样式 */
.recognition-status {
    margin: 10px 0;
    padding: 8px 15px;
    border-radius: 20px;
    font-weight: bold;
    text-align: center;
}

/* 成功状态 */
.success-status {
    color: #4cd964;
    background-color: rgba(76, 217, 100, 0.1);
}

/* 失败状态 */
.failed-status {
    color: #ff3b30;
    background-color: rgba(255, 59, 48, 0.1);
}

/* 上传按钮 */
.upload-btn {
    margin: 15px 0;
    background-color: #007aff;
    color: white;
    border: none;
    border-radius: 6px;
    padding: 10px 20px;
    font-size: 16px;
}

/* 禁用状态 */
.upload-btn[disabled] {
    background-color: #cccccc;
    color: #666;
}

/* 从底部向上滑动的过渡效果 */
.slide-up-enter-active, 
.slide-up-leave-active {
    transition: all 0.3s ease;
}

.slide-up-enter-from, 
.slide-up-leave-to {
    opacity: 0;
}

.slide-up-enter-from .modal-content, 
.slide-up-leave-to .modal-content {
    transform: translateY(100%);
}

.slide-up-enter-to .modal-content, 
.slide-up-leave-from .modal-content {
    transform: translateY(0);
}

.slide-up-enter-from .modal-mask, 
.slide-up-leave-to .modal-mask {
    opacity: 0;
}

/* 加载指示器容器 */
.loading-container {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: rgba(255, 255, 255, 0.9);
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    z-index: 1001;
}

/* 加载转圈动画 */
.loading-spinner {
    width: 40px;
    height: 40px;
    border: 4px solid #f3f3f3;
    border-top: 4px solid #007aff;
    border-radius: 50%;
    animation: spin 1s linear infinite;
    margin-bottom: 15px;
}

@keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}

/* 加载文本 */
.loading-text {
    color: #333;
    font-size: 16px;
}

/* 语音识别结果输入框样式 */
.recognition-result-input {
    width: 90%;
    margin: 15px 0;
    padding: 12px 15px;
    border: 1px solid #ddd;
    border-radius: 8px;
    font-size: 16px;
    color: #333;
    background-color: #f9f9f9;
    text-align: center;
    display: none; /* 隐藏输入框 */
}

/* 删除底部识别结果容器样式 */
.recognition-result-container {
    display: none;
}

/* 更平滑的图标脉动动画 */
@keyframes smooth-pulse {
    0%, 100% {
        transform: scale(1);
        box-shadow: 0 0 10px rgba(0, 122, 255, 0.5);
    }
    50% {
        transform: scale(1.08);
        box-shadow: 0 0 25px rgba(0, 122, 255, 0.8);
    }
}

/* 更平滑的第一层波纹动画 */
@keyframes smooth-ripple-1 {
    0% {
        transform: translate(-50%, -50%) scale(0.8);
        opacity: 0.7;
    }
    100% {
        transform: translate(-50%, -50%) scale(1.6);
        opacity: 0;
    }
}

/* 更平滑的第二层波纹动画 */
@keyframes smooth-ripple-2 {
    0% {
        transform: translate(-50%, -50%) scale(0.8);
        opacity: 0.6;
    }
    100% {
        transform: translate(-50%, -50%) scale(1.8);
        opacity: 0;
    }
}

/* 更平滑的第三层波纹动画 */
@keyframes smooth-ripple-3 {
    0% {
        transform: translate(-50%, -50%) scale(0.9);
        opacity: 0.5;
    }
    100% {
        transform: translate(-50%, -50%) scale(1.8);
        opacity: 0;
    }
}

/* 第四层波纹动画 */
@keyframes smooth-ripple-4 {
    0% {
        transform: translate(-50%, -50%) scale(0.8);
        opacity: 0.4;
    }
    100% {
        transform: translate(-50%, -50%) scale(2);
        opacity: 0;
    }
}

/* 第五层波纹动画 */
@keyframes smooth-ripple-5 {
    0% {
        transform: translate(-50%, -50%) scale(0.7);
        opacity: 0.3;
    }
    100% {
        transform: translate(-50%, -50%) scale(2.2);
        opacity: 0;
    }
}
</style> 